package cc.magicjson.manager;

import cc.magicjson.utils.PortUtils;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServer;
import org.springframework.boot.web.server.WebServerFactory;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;

import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ServerManager {
    private static final Logger LOGGER = Logger.getLogger(ServerManager.class.getName());

    public static void switchToDefaultPort(ConfigurableApplicationContext context, int defaultPort) {
        try {
            PortUtils.terminateProcessOnPort(defaultPort);
            PortUtils.waitForPortToBeAvailable(defaultPort);
            rebindToDefaultPort(context, defaultPort);
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Failed to switch to default port", e);
        }
    }

    private static void rebindToDefaultPort(ConfigurableApplicationContext context, int defaultPort) {
        try {
            WebServerFactory webServerFactory = getWebServerFactory(context);
            ((TomcatServletWebServerFactory) webServerFactory).setPort(defaultPort);
            WebServer webServer = ((TomcatServletWebServerFactory) webServerFactory).getWebServer(invokeSelfInitialize((ServletWebServerApplicationContext) context));
            webServer.start();
            ((ServletWebServerApplicationContext) context).getWebServer().stop();
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Failed to rebind to default port", e);
            throw new RuntimeException("Rebinding to default port failed", e);
        }
    }


    private static ServletContextInitializer invokeSelfInitialize(ServletWebServerApplicationContext context) {
        try {
            Method method = ServletWebServerApplicationContext.class.getDeclaredMethod("getSelfInitializer");
            method.setAccessible(true);
            return (ServletContextInitializer) method.invoke(context);
        } catch (Exception e) {
            throw new RuntimeException("Failed to invoke self-initialize", e);
        }
    }

    private static WebServerFactory getWebServerFactory(ConfigurableApplicationContext context) {
        String[] beanNames = context.getBeanFactory().getBeanNamesForType(WebServerFactory.class);
        return context.getBeanFactory().getBean(beanNames[0], WebServerFactory.class);
    }
}
